#ifndef TB_BASE_BUFFER_IMPL_HPP__
#define TB_BASE_BUFFER_IMPL_HPP__

#include <list>

#include "base.hpp"
#include "assert.hpp"
#include "blob.hpp"

TB_NAMESPACE_BEGIN

struct MemoryBufferImpl {
	MemoryBufferImpl()
		: blobCapacity_(1024),
		readOffset_(0),
		writeOffset_(0),
		readIndex_(0),
		writeIndex_(0),
		readingBlob_(1),
    writingBlob_(0) {}

	typedef std::list<Blob> Blobs_T;
	Blobs_T blobs_;
	std::allocator<char> allocator_;
	uint32 blobCapacity_;
	uint32 readOffset_;
	uint32 writeOffset_;
  uint32 readIndex_;
  uint32 writeIndex_;
  uint32 readingBlob_;
  uint32 writingBlob_;

	Blob& BlobAt(uint32 idx) {
		uint32 _idx = 0;
		for (Blobs_T::iterator it = blobs_.begin(); 
			it != blobs_.end(); 
			++it, ++_idx) {
			if (_idx == idx) {
				return *it;
			}
		}
		TB_ASSERT(false && "out of range");
		return blobs_.front();
	}

	void InternalCheck() const {
		DCHECK(writeIndex_ < readIndex_)
			<< "write index must greate or equal to read index";
	}

	uint32 CanPrependSize() const {
		uint32 result = readingBlob_ > 0 ?
			(readingBlob_ - 1) * blobCapacity_ : 0;

		return result + readOffset_;
	}

	tuple<const char*, uint32> InternalReadValue(uint32 len, bool fwd) {
    if (InternalSize() < len) {
      return tuple<const char*, uint32>(nullptr, 0);
		}

		if (readingBlob_ <= blobs_.size()) {
			TB_ASSERT(readingBlob_ <= blobs_.size());

			Blob& b = BlobAt(readingBlob_ - 1);
			if (b.size() == readOffset_) {
				readingBlob_++;
				readOffset_ = 0;
				if (readingBlob_ <= writingBlob_) {
					b = BlobAt(readingBlob_ - 1);
				} else {
					return tuple<const char*, uint32>(nullptr, 0);
				}
			}

			uint32 dataSize = std::min(b.size() - readOffset_, writeIndex_ - readIndex_);
			if (dataSize >= len) {
				uint32 roff = readOffset_;
				if (fwd) {
					readOffset_ += len;
					readIndex_ += len;
				}
				return tuple<const char*, uint32>(b.content() + roff, len);
			}
			
		}

		return tuple<const char*, uint32>(nullptr, 0);
	}

	void InternalResize(uint32 len, bool fill) {
		uint32 acc = 0;

		for (uint32 i = (writingBlob_ == 0 ? 1 : writingBlob_);
			i < blobs_.size(); ++i) {
			if (blobs_.size() == 0)
			acc += blobCapacity_;
		}

		if (!blobs_.empty()) acc += writeIndex_ + (blobCapacity_ - writeOffset_);

		Blob::buffer_ptr buf;
		while (true) {
			if (acc >= len) {
				break;
			}

			buf = boost::allocate_shared<char[]>(allocator_, blobCapacity_);
			blobs_.push_back(std::move(Blob(buf, blobCapacity_)));
			if (fill) {
				blobs_.back().setSize(blobCapacity_);
			} else {
				blobs_.back().setSize(0);
			}
			acc += blobCapacity_;
		}

		if (!blobs_.empty() && writingBlob_ <= 0) {
			writingBlob_ = 1;
		}
	}

	uint32 InternalSize() const {
		return writeIndex_ - readIndex_;
	}

	void CalBlobIndexAndOffset(uint32 pos, uint32& blobIndex, uint32& blobOffset) {
		uint32 index = 0;
		uint32 offset = pos;
		uint32 acc = 0;

		uint32 _idx = 1;
		for (Blobs_T::iterator it = blobs_.begin();
			it != blobs_.end();
			++it, ++_idx) {
			Blob& b = *it;
			acc += b.size();
			if (acc >= pos) {
				index = _idx;
				break;
			}
			offset -= b.size();
		}

		if (index == 0 && blobs_.size() >= 1) {
			index = 1;
		}

		blobIndex = index;
		blobOffset = offset;
	}
};

TB_NAMESPACE_END

#endif // TB_BASE_BUFFER_IMPL_HPP__
